package Cluedo.GUI2;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.io.File;
import java.io.IOException;
import java.util.Properties;
import java.util.Vector;

import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;

import Cluedo.API.CluedoConfig;
import Cluedo.API.CluedoGUIListener;
import Cluedo.API.CluedoListener;
import Cluedo.API.Constants;
import Cluedo.API.DisproofAlert;
import Cluedo.API.LanguagePack;
import Cluedo.API.Message;
import Cluedo.API.Resources;
import Cluedo.API.SoundEngine;
import Cluedo.API.SuggestionAlert;
import Cluedo.Controller.GameController;

/**
 * This View is a simple GUI for Increment 1.
 *
 * @author alex
 */
public class AdvanceGUI extends CluedoGUIListener 
		implements ActionListener, MouseMotionListener {
	
	JFrame parent = null;
	
	JMenuItem optionsEnableSoundMenuItem;

    BorderLayout layout = new BorderLayout();
    
	String imageFileName = "";
	
	Cluedo.GUI2.Board board = null;
	Cluedo.GUI2.Toolbar toolbar = null;
	CluedoConfig gameConfig = null;
	
	GameController gameController = null;
	
	JLabel statusBarLabel = new JLabel("");
	JLabel playerPositionLabel = new JLabel("Position");
	
	boolean loaded = false;
	boolean enableSound = true;
	
	private static final String DEFAULT = "main_gui";
	

    /**
     *  Fake colours -- bound to players, playerColour == fakeColours[playerIndex]
     */
	  Color fakeColours[] = new Color[] { Color.white, Color.blue, Color.green, Color.red, Color.yellow, Color.magenta, Color.orange };

	  
	/** 
	 * Plays sounds to give the player a sense of the atmosphere
	 * in the cluedo game
	 */
	AmbiencePlayer ambiencePlayer = null;
	/** 
	 * Whether to play ambience sounds or not.
	 */
	boolean ambienceOn = true;
	  
	  
	public void mouseDragged(MouseEvent e) {
		
	}

	public void mouseMoved(MouseEvent e) {
		playerPositionLabel.setText( board.getFloatingPosition((int)e.getX(), (int)e.getY()) );
	}


	private void enableSound(boolean value) {
		enableSound = value;
		SoundEngine.setPlaybackStatus(value);
		if(optionsEnableSoundMenuItem != null)
		    optionsEnableSoundMenuItem.setSelected(value);
	}

	/**
	 * Sets up the main graphical window
	 * 
	 * @param frameTitle
	 * Title of game window
	 * 
	 * @param config
	 * Cluedo config with all players etc
	 * 
	 * @param controller
	 * Game controller
	 * @param loaded
	 * Whether this game was loaded from disk
	 * @param enableSound
	 * Whether to enable sound in this game
	 * @param parent
	 * Used for loading back the GameMenu
	 * 
	 */
	public AdvanceGUI(String frameTitle, CluedoConfig config, GameController controller, boolean loaded, boolean enableSound, JFrame parent) {
		super(frameTitle);
		gameConfig = config;
		gameController = controller;
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		imageFileName = "./Resources/Maps/" + config.getMapVersion() + ".gif";
		this.loaded = loaded;

		this.enableSound= enableSound;
		this.parent = parent;

		if(SoundEngine.threadAlive == false) {
		    SoundEngine soundEngine = new SoundEngine();
			soundEngine.start();
		}
		
		enableSound(this.enableSound);		
		init();
		
		
		if(ambienceOn == true) {
		    ambiencePlayer = new AmbiencePlayer();
		    ambiencePlayer.start();
		}

	}
	public JFrame getCurrentPlayer() {
		return this;
	}
	
	/**
	 * Closes this GUI and opens the main menu.
	 * 
	 * @param newGame
	 * Whether to also open the newGame menu
	 */
	public void closeGame(int newGame) {
		
		this.setVisible(false);
		
		parent.setVisible(true);
		
		if(newGame == 1) {
			ActionEvent a = new ActionEvent(this, 1, "new_game");
			((GameMenu)parent).actionPerformed(a);
		}
		else if(newGame == 2) {
			ActionEvent a = new ActionEvent(this, 1, "load_game");
			((GameMenu)parent).actionPerformed(a);			
		}
		
		ambiencePlayer.done = true;
		
		
		dispose();
	}
	
  /**
   * We detect an event in the cluedo game
   *
   */  
	CluedoListener cluedoListener = new CluedoListener() {
		public void notifyAlert(Message m) {
			if(m.type.equals("commenceGame")) {
				statusBarLabel.setText(addArguments(
						LanguagePack.getString("now_turn_for", "main_gui"), new String[] {gameController.getBoard().getCurrentPlayer().getRealName()} ));
			}
			else if(m.type.equals("nextPlayer")) {
				if(gameController.getBoard().getCurrentPlayer().isInteractive() == false) {
					statusBarLabel.setText(addArguments(
							LanguagePack.getString("is_out_of_game", "main_gui"), new String[] {gameController.getBoard().getCurrentPlayer().getRealName()} ));
				}
				else {
					statusBarLabel.setText(addArguments(
							LanguagePack.getString("now_turn_for", "main_gui"), new String[] {gameController.getBoard().getCurrentPlayer().getRealName()} ));
				}
				board.repaint();
				board.moveCompleted();				
			}
			else if(m.type.equals("moveAlert")) {
				board.repaint();
			}
			else if(m.type.equals("suggestionAlert")) {
				SuggestionAlert alert = (SuggestionAlert)m.parameters.elementAt(0);
				
			    // no candidate found for disproof
			    if(m.data.equals("") && alert.disproveable == true) {
					
					System.out.println("[AdvanceGUI:notifyAlert] Heard a suggestion alert.");
					
					try {
						Vector cards = new Vector();
						cards.add(alert.who);
						cards.add(alert.what);
						cards.add(alert.where);
						
						Message disproofAlert = new Message();
						disproofAlert.parameters.add( new DisproofAlert( "", alert, "" ) );
						// Get the game controller to relay the message to all listeners
						gameController.relayMessage(disproofAlert);

						
					}
					catch(Exception e) {
						e.printStackTrace();	
					}
					
				}
				else
					if(gameController.getBoard().getPlayer(m.data).getPlayerType() == 0) {
						System.out.println("[AdvanceGUI:notifyAlert] Heard a suggestion alert.");
						try {
							Vector cards = new Vector();
							cards.add(alert.who);
							cards.add(alert.what);
							cards.add(alert.where);
							
//							DisproofWindow disproofWindow = new DisproofWindow(getCurrentPlayer(), m.data, cards, alert, gameController);
//							disproofWindow.show();
							
						}
						catch(Exception e) {
							e.printStackTrace();	
						}
					}
				
			}
			/*
			 * TODO: this is a BUG -- it's not the current player ALWAYS?!!
			 */
		
			else if(m.type.equals("winGame") || gameController.getBoard().getGameFinished() == true) {
				statusBarLabel.setText(addArguments(
						LanguagePack.getString("won_the_game", "main_gui"), new String[] {gameController.getBoard().getCurrentPlayer().getRealName()} ));
			}


		}
	};


	private void saveGame(String fileName) {
		String output = "";

		toolbar.writeBackDPadPanel();
		
		try {
			Resources.dumpTextFile(fileName, gameController.getBoard().toXML(gameConfig.getMapVersion()));
		}
		catch(IOException err) {
			System.out.println("[Simple:localSaveGame] can't write save game.");
		}

	}
	

	private void showInstructionsFile() {
		String output = "";
		
		/** 
		  * Execute the method simply with the filename as the argument
		  * ie openDocument("index.html");
		  *
		  */
	    try {
	        Resources.openDocument(Constants.HTML_ROOT);
	    }
	    catch(Exception exp) {
	        JOptionPane.showMessageDialog(null, "Unable to find help file, make sure you have it installed.", "Error", JOptionPane.INFORMATION_MESSAGE);
	    }
	}


	public void actionPerformed(ActionEvent e) {
		String button = e.getActionCommand();

		if (button.equals("quit")) {
			
			
			int selectedValue = JOptionPane.showConfirmDialog(null,
					LanguagePack.getString("quit_game_confirm", DEFAULT), "Confirm exit", JOptionPane.OK_CANCEL_OPTION);
			if(selectedValue == JOptionPane.OK_OPTION) {				
				if(parent == null) {
					System.exit(0);
				}
				else {
					closeGame(0);
				}
			}
		}
		else if (button.equals("toggle_sound")) {
			if(optionsEnableSoundMenuItem.isSelected() == true) {
				optionsEnableSoundMenuItem.setSelected(false);
				enableSound (false);
			}
			else if(optionsEnableSoundMenuItem.isSelected() == false) {
				optionsEnableSoundMenuItem.setSelected(true);
				enableSound (true);
			}

		}
		else if (e.getActionCommand().equals("open_help")) {
			showInstructionsFile();
		}
		else if (e.getActionCommand().equals("save_game")) {
			
			JFileChooser chooser = new JFileChooser(new File ("Resources/Saves/"));
			// Note: source for ExampleFileFilter can be found in FileChooserDemo,
			// under the demo/jfc directory in the Java 2 SDK, Standard Edition.
			Cluedo.API.ExampleFileFilter filter = new Cluedo.API.ExampleFileFilter ();
			filter.addExtension("xml");
			filter.setDescription("XML save games");
			chooser.setFileFilter(filter);

			int returnVal = chooser.showSaveDialog(this);
			if(returnVal == JFileChooser.APPROVE_OPTION && chooser.getSelectedFile() != null) {
			   System.out.println("You chose to save to this file: " +
			   			chooser.getSelectedFile().getPath());

			    if(!chooser.getSelectedFile().getPath().endsWith(".xml"))
			        saveGame(chooser.getSelectedFile().getPath() + ".xml");
			    else
			        saveGame(chooser.getSelectedFile().getPath());
			}
			
		}
		else if (e.getActionCommand().equals("load_game")) {
			int selectedValue = JOptionPane.showConfirmDialog(null,
					LanguagePack.getString("load_game_confirm", DEFAULT), "Confirm exit", JOptionPane.OK_CANCEL_OPTION);
	          		if(selectedValue == JOptionPane.OK_OPTION) {
	          			closeGame( 2 );
	          		}
			
		}
		else if( e.getActionCommand().equals("new_game")) {

			int selectedValue = JOptionPane.showConfirmDialog(null,
					LanguagePack.getString("new_game_confirm", DEFAULT), "Confirm exit", JOptionPane.OK_CANCEL_OPTION);
	          		if(selectedValue == JOptionPane.OK_OPTION) {
	          			closeGame( 1 );
	          		}
		}
		
	}

	/**
	 * Gets the game ready and paints all the graphical items on the screen
	 * 
	 *
	 */
	private void init() {
		setGame();
		
		gameController.bindListener(cluedoListener);
		
		Container content = getContentPane();
		content.setLayout(layout);
		JPanel boardHolder = new JPanel();
		JPanel toolbarHolder = new JPanel();

		// Board
		
		board = new Cluedo.GUI2.Board(imageFileName, gameConfig, gameController);
		board. setLoadedMapAreas(gameConfig.getLoadedMapAreas());
		board. addMouseMotionListener(this);

		boardHolder. add( board );
	
	// Toolbar
	
		toolbar = new Cluedo.GUI2.Toolbar(gameConfig, gameController, this);
		toolbarHolder. setLayout(new BorderLayout());
		toolbarHolder. add(toolbar, BorderLayout.NORTH);

		



	// Menu system

		JMenuBar menuBar = new JMenuBar();
		JMenu fileMenu = new JMenu(LanguagePack.getString("file_menu", "main_gui"));
		JMenu optionsMenu = new JMenu(LanguagePack.getString("options_menu", "main_gui"));
		JMenu helpMenu = new JMenu(LanguagePack.getString("help_menu", "main_gui"));
		

		JMenuItem fileNewMenuItem = new JMenuItem(LanguagePack.getString("new_game", "main_gui"));
		fileNewMenuItem.setActionCommand("new_game");
		JMenuItem fileLoadMenuItem = new JMenuItem(LanguagePack.getString("load_game", "main_gui"));
		fileLoadMenuItem.setActionCommand("load_game");
		JMenuItem fileSaveMenuItem = new JMenuItem(LanguagePack.getString("save_game", "main_gui"));
		fileSaveMenuItem.setActionCommand("save_game");
		JMenuItem fileQuitMenuItem = new JMenuItem(LanguagePack.getString("quit", "main_gui"));
		fileQuitMenuItem.setActionCommand("quit");
				
		JMenuItem helpShowHelpMenuItem = new JMenuItem(LanguagePack.getString("open_help", "main_gui"));
		helpShowHelpMenuItem.setActionCommand("open_help");

		optionsEnableSoundMenuItem = new JMenuItem(LanguagePack.getString("toggle_sound", "main_gui"));
		//optionsEnableSoundMenuItem.setSelected(true);
		optionsEnableSoundMenuItem.setActionCommand("toggle_sound");


		fileQuitMenuItem.addActionListener(this);
		fileSaveMenuItem.addActionListener(this);
		fileNewMenuItem.addActionListener(this);
		fileLoadMenuItem.addActionListener(this);

		optionsEnableSoundMenuItem.addActionListener(this);

		helpShowHelpMenuItem.addActionListener(this);


		fileMenu.add( fileNewMenuItem );
		fileMenu.add( fileLoadMenuItem );
		fileMenu.add( fileSaveMenuItem );
		fileMenu.add( fileQuitMenuItem );		

		helpMenu.add( helpShowHelpMenuItem );

		optionsMenu.add( optionsEnableSoundMenuItem );

		menuBar.add( fileMenu );
		menuBar.add( optionsMenu );
		menuBar.add( helpMenu );

	// Status bar
	
		JPanel statusBar = new JPanel();
		statusBar.setLayout(new BorderLayout());
		statusBarLabel.setText("New game started..");
		playerPositionLabel.setText("Position: (none)");

		statusBar.add(statusBarLabel, BorderLayout.WEST);		
		statusBar.add(playerPositionLabel, BorderLayout.EAST);


	// Content panel

		content.add(menuBar, BorderLayout.NORTH);
		content.add(statusBar, BorderLayout.SOUTH);

		content.add(toolbarHolder, BorderLayout.EAST);
		content.add(new JScrollPane(board), BorderLayout.CENTER);				

		
		setResizable(false);
		pack();		

		Message m = new Message();
		m.type = "nextPlayer";
		cluedoListener.notifyAlert(m);
		sendStartSignal();

	}
	
	void setUplink(CluedoConfig gameConfig, GameController gameController) {
		this.gameController = gameController;
		this.gameConfig = gameConfig;
	}


	private void setGame() {

		Vector params = null;
		
		// Add players
		gameController.directCommand("insertPlayers", gameConfig.getPlayers());
		

		// Add squares
		gameConfig.loadBindings();
		
		// Configure map bindings for model
		gameController.directCommand("insertSquares", gameConfig.getMapNodes());
		gameController.directCommand("createBindings", gameConfig.getBindings());
	
		// If cards not loaded, load them	
		if(gameConfig.getCards().countCards() == 0) {
		    gameConfig.setCardSet("default");
			gameConfig.loadCards();
			System.out.println("Taking action against broken card set in AdvanceGUI.");
		}
		
		// Add cards to game board
		params = new Vector();
		params.add(gameConfig.getCards());
        	
		gameController.directCommand("insertCards", params);
		
		// Setup new detective pads
		if(loaded == false)
			gameController.directCommand("loadNewDetectivePads", null);


		// Deal cards to players and set up envelope
		if(loaded == false) {
			params = new Vector();
			params.add(gameConfig.getCards());
			gameController.directCommand("setEnvelopeCards", params);
			gameController.directCommand("dealCardsToPlayers" , null);
		}
		

		params = new Vector();		
		// Put players into their starting positions
		if(loaded == false)
			params.add("defined");
		else
			params.add("existing");

		gameController.directCommand("positionPlayers", params);
		
		// Chose a player to start the game
		if(loaded == false) {
			gameController.directCommand("chooseFirstPlayer", null);
		}

	
	}
	
	/**
	 * Otherwise the AIs begin without us!!
	 */
	void sendStartSignal() {
		// Sends a nextPlayer message to all clients representing the start of a new game.
		gameController.directCommand("commenceGame", null);	
	}
	
	
	
	/**
	 * @param string
	 * @return
	 */
	private static String addBreaks(String string) {
		string = string.replaceAll("@@", "<br>");
		string = Constants.HTML_ON + string + Constants.HTML_OFF;
		return string;
	}
  
	  
	/**
	 * Adds relevant arguments to string from String array 
	 * 
	 * @param string
	 * @param names2
	 * @return formatted string
	 */
	private static String addArguments(String string, String[] names2) {
		for(int i=0;i<names2.length;i++)
			string = string.replaceAll("%"+(i+1), names2[i]);
		if(string.indexOf("@@") > -1) {
			string = addBreaks(string);
		}
		return string;
	}
	
  
  private static String lookup(String function, String group) {
  	return LanguagePack.getString(function, group);
  }
  
  
  class AmbiencePlayer extends Thread {
      private int defaultPause = 30000;
      private int randomExtraPause = 30000;

      String nextSound = "";
      boolean done = false;
      
      public void run() {
          while(done == false) {
              randomExtraPause = (int)(Math.random()*10000);

              if(nextSound.equals("")) {
                  Properties sounds = SoundEngine.getBindings();
                  Object[] soundSet = sounds.keySet().toArray();
                  
                  int loopClosure = 40;
                  while(nextSound.startsWith("ambience.") == false && loopClosure > 0) {
                      nextSound = (String)soundSet[ ((int)(Math.random() * soundSet.length)) ];
                      loopClosure--;
                  }
              }
              try {
              	
            //      System.out.println("[AmbiencePlayer:wait] " + (defaultPause + randomExtraPause));
                  Thread.sleep(defaultPause + randomExtraPause);
              }
              catch(Exception e) {
                  System.out.println("Quitting ambience engine");
                  e.printStackTrace();
                  }
              if(SoundEngine.soundExists(nextSound) == false) {
              	done = true;
                System.out.println("Quitting ambience engine - can't find sound: " + nextSound);
              	return;
              }
/*              if(nextSound.equals("")) {
                  done = true;
              }*/

              // TODO: Enable ambience player
        //      System.out.println("[AmbiencePlayer:play] " + nextSound);

              if(enableSound == true)
                  Cluedo.API.SoundEngine.bufferSound(nextSound);

              nextSound = "";
          }
          System.out.println("Quitting ambience engine");
      }
  }
}
